#include <iostream>
#include <algorithm>
#include <stdio.h>
#include "Tictactoe.h"

using std::cout;
using std::cin;

Tictactoe::Tictactoe()
{
    // Inicializa o tabuleiro como zero
    for( int i = 0; i < 9; i++ )
        _board.push_back( 0 );

    // Jogo comeca com player 1
    _turn = 1;
}


Tictactoe::~Tictactoe()
{
}

bool Tictactoe::playerWins( std::vector<int> board, int player )
{
    // Diagonals
    if( board[0] == player && board[4] == player &&
            board[8] == player )
        return true;
    if( board[2] == player && board[4] == player &&
            board[6] == player )
        return true;

    // Lines
    if( board[0] == player && board[1] == player &&
            board[2] == player )
        return true;
    if( board[3] == player && board[4] == player &&
            board[5] == player )
        return true;
    if( board[6] == player && board[7] == player &&
            board[8] == player )
        return true;

    // Columns
    if( board[0] == player && board[3] == player &&
            board[6] == player )
        return true;
    if( board[1] == player && board[4] == player &&
            board[7] == player )
        return true;
    if( board[2] == player && board[5] == player &&
            board[8] == player )
        return true;

	return false;
}

bool Tictactoe::hasGameEnded( std::vector<int> board )
{
    // Check if player 1 won
    if( playerWins( board, 1) )
        return true;

    // Check if player 2 won
    if( playerWins( board, 2) )
        return true;

    // Check if there is empty places
    for( int i = 0; i < 9; i++ )
        if( board[i] == 0 )
            return false;

    // Velha!
    return true;
}

int Tictactoe::utility( std::vector<int> board )
{
    // Check if player 1 won
    if( playerWins( board, 1 ) )
        return -1;

    // Check if player 2 won
    if( playerWins( board, 2 ) )
        return 1;

    // Velha!
    return 0;
}


void Tictactoe::printBoard( std::vector<int> board )
{
    for( int i = 0; i < 9; i++ )
    {
        if( i % 3 == 0 )
            cout << "\n";

        switch( board[i] )
        {
            case 0:
                cout << "  ";
                break;
            case 1:
                cout << "X ";
                break;
            case 2:
                cout << "O ";
                break;
        }
    }
    cout << "\n";
}

void Tictactoe::playerPlays( int player, int pos )
{
    int i, j;

    if( _board[pos] != 0 )
        return;

    // Player plays
    _board[pos] = player;

    // Change turn
    if( player == 1 )
        _turn = 2;
    if( player == 2 )
        _turn = 1;
}


int Tictactoe::getTurn()
{
    return _turn;
}


void Tictactoe::getBestMove( std::vector<std::pair<int,int> > moves, int player, int& bestMove, int& bestResult )
{
    std::pair<int,int> p;
    bestMove = 0;
    p.first = moves[0].first;
    p.second = moves[0].second;

    for( int k = 1; k < 9; k++ )
    {
        if( player == 2 && moves[k].first > p.first )
        {
            p.first = moves[k].first;
            bestMove = k;
            p.second = moves[k].second;
        }
        if( player == 2 && moves[k].first == p.first )
        {
            if( moves[k].second < p.second )
            {
                p.first = moves[k].first;
                bestMove = k;
                p.second = moves[k].second;
            }
        }
        if( player == 1 && moves[k].first < p.first )
        {
            p.first = moves[k].first;
            bestMove = k;
            p.second = moves[k].second;
        }
        if( player == 1 && moves[k].first == p.first )
        {
            if( moves[k].second < p.second )
            {
                p.first = moves[k].first;
                bestMove = k;
                p.second = moves[k].second;
            }
        }
    }
    bestResult = p.first;
}


int Tictactoe::minimax( std::vector<int> brd )
{
    std::vector<std::pair<int,int> > bestMove;
    for( int k = 0; k < 9; k++ )
    {
        std::pair<int, int> p;
        p.first = -2; // utilidade
        p.second = 0; // nivel de profundidade na arvore
        bestMove.push_back( p );
    }

    for( int k = 0; k < 9; k++ )
    {
        // Se posicao ja ocupada, continue analise com a proxima livre
        if( brd[k] != 0 )
            continue;

        // Copia o tabuleiro
        std::vector<int> brdcp = brd;

        // inicia o calculo do minimax como nivel 0
        bestMove[k] = max( brdcp, 0, k );
    }

    int play, value;
//    fprintf(stderr, "\n");
//    for( int k = 0; k < 9; k++ )
//    {
//        fprintf(stderr, "[%d] = %d, %d\n", k, bestMove[k].first, bestMove[k].second );
//    }
//    fprintf(stderr, "\n" );
    getBestMove( bestMove, 2, play, value );
    return play;
}


std::pair<int,int> Tictactoe::max( std::vector<int> brd, int level, int pos )
{
    // Incrementa o nivel de profundidade
    level++;

    // computador faz jogada
    brd[pos] = 2;

    //printBoard( brd );

    if( hasGameEnded( brd ) )
    {
        std::pair<int, int> p;
        p.first = utility( brd );
        p.second = level;
        return p;
    }

    std::vector<std::pair<int, int> > bestMove;
    for( int k = 0; k < 9; k++ )
    {
        std::pair<int, int> p;
        p.first = 5;
        p.second = level;
        bestMove.push_back( p );
    }

    for( int k = 0; k < 9; k++ )
    {
        // Se posicao ja ocupada, continue analise com a proxima livre
        if( brd[k] != 0 )
            continue;

        // Copia o tabuleiro
        std::vector<int> brdcp = brd;

        // inicia o calculo do minimax
        bestMove[k] = min( brdcp, level, k );
    }

    int play, value;
    getBestMove( bestMove, 1, play, value );
    return bestMove[play];
}


std::pair<int,int> Tictactoe::min( std::vector<int> brd, int level, int pos )
{
    // Incrementa o nivel de profundidade
    level++;

    // humano faz jogada
    brd[pos] = 1;

    //printBoard( brd );

    if( hasGameEnded( brd ) )
    {
        std::pair<int, int> p;
        p.first = utility( brd );
        p.second = level;
        return p;
    }

    std::vector<std::pair<int, int> > bestMove;
    for( int k = 0; k < 9; k++ )
    {
        std::pair<int,int> p;
        p.first = -5;
        p.second = level;
        bestMove.push_back( p );
    }

    for( int k = 0; k < 9; k++ )
    {
        // Se posicao ja ocupada, continue analise com a proxima livre
        if( brd[k] != 0 )
            continue;

        // Copia o tabuleiro
        std::vector<int> brdcp = brd;

        // inicia o calculo do minimax
        bestMove[k] = max( brdcp, level, k );
    }

    int play, value;
    getBestMove( bestMove, 2, play, value );
    return bestMove[play];
}

std::vector<int> Tictactoe::getBoard()
{
    return _board;
}

